package com.example.kotlindemo.demo

import com.example.kotlindemo.Utils

/**
 * Author 87231
 * 2022/8/31 23:07
 *
 * Unit2
 * describe：对象表达式和对象声明
 * Kotlin用对象表达式和对象声明来实现创建一个对某个子类做了轻微改动的类的对象，
 * 且不需要去声明一个新的子类
 * 一、对象表达式
 * 二、对象声明
 * 三、伴生对象
 *
 **/
class Unit12 {
    /**
     * 一、对象表达式
     * 实现一个匿名内部类的对象用于方法的参数中
     */
    fun testA() {
        Utils().addCallback(object : CallBack {
            override fun onA(str: String) {
                println(str)
            }
        }
        )
    }

    //    对象可以继承于某一个基类，也可以实现某一个接口
    open class A(x: Int) {
        public open var y: Int = x
    }

    interface B {
        fun fooB(str: String)
    }


    fun fooB() {
        var ab = object : Unit12.A(12), Unit12.B {
            override fun fooB(str: String) {
                println(str)
            }

            override var y: Int
                get() = super.y
                set(value) {}
        }
    }

    //    匿名对象可以作用只在本地和私有作用域中声明的类型。如果你是用匿名对象作为共有函数的返回类型或者
//    作用公有属性的类型，那么该函数的实际类型会是匿名函数对象声明的超类型，如果你没有声明任何超类型，
//    就会是Any。在匿名函数中添加的成员将无法访问。
    class C {
        private fun fooA() = object {
            var x: Int = 123
        }

        fun fooB() = object {
            var x: Int = 321
        }

        fun fooC() {
            var a = fooA().x
//            var b =fooB().x //报错，找不到x
        }
    }

    //    在对象表达式中可以方便的访问到作用域中的其他变量
    open class D {
        open var name = "D"
        open fun fooA(str: String) {
            println(str)
        }

        open fun fooB(str: String) {
            println(str)
        }
    }

    fun addD(d: D) {
        d.fooA("fooA")
        d.fooB("fooB")
    }

    fun testD() {
        addD(object : D() {
            override fun fooA(str: String) {
                println(str)
            }

            override var name: String
                get() = super.name
                set(value) {}

            override fun fooB(str: String) {
                println(str)
            }
        })
    }

    /**
     * 二、对象声明
     */
//    使用object关键字来声明一个对象，可以应用于生成单例子
    object E {
        var name = "tom"
        fun fooA() {
            println("fooA")
        }

        fun fooB() {
            println("fooB")
        }
    }

    fun testE() {
        var e = E
        var e1 = E
        e.fooA()
        e.fooB()
        e.name = "xiaohong"
        println(e.name)
        e1.fooA()
        e1.fooB()
        println(e1.name)
//  两个不同的变量获取的值相同

//  对象可以有超类型
    }
    object F :D(){
        override fun fooA(str: String) {
            super.fooA(str)
        }

        override fun fooB(str: String) {
            super.fooB(str)
        }

        override var name: String
            get() = super.name
            set(value) {}

    }
    fun testF(){
        F.fooA("F.fooA")
        F.fooB("F.fooB")
    }
//    在声明一个类的内部对象时，这个对象不能直接通过这个了类的实例对象访问，而是通过类名访问到该对象
//    同样该对象也不能直接访问到该外部类的属性和方法
    class G{
        fun fooA(){
            println("G")
        }
    var name = "G"
    object OG{
        fun fooOG(){
            println("OG")
        }
    }
    }
    var testG:() ->Unit ={
        G.OG.fooOG()//直接通过类型访问到内部的对象
    }
    /**
     * 三、伴生对象
     * 内部类的对象声明可以使用companion关键字，这样就可以跟外部类关联在一起了，我们就可以直接通过外部类访问到哪对象的内部元素
     * 关键字companion关键字只能使用一次
     * 伴生对象的名称可以直接省略掉
     */
    class H{
        companion object{
            fun fooA(){
                println("this is companion")
            }
        }
    }
    var testH:()->Unit={
        H.fooA()
    }

}
